
// M_misc.c

#ifdef __NeXT__
#include <libc.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#ifndef ARM9
#include <direct.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#endif
#ifdef ARM9
#include <unistd.h>
#endif

#ifdef WIN32
#define	strcasecmp strcmpi
#define	strncasecmp strnicmp
#endif

#include <ctype.h>

#include "DoomDef.h"
#include "soundst.h"

int myargc;
char **myargv;

//---------------------------------------------------------------------------
//
// FUNC M_ValidEpisodeMap
//
//---------------------------------------------------------------------------

boolean M_ValidEpisodeMap(int episode, int map)
{
	if(episode < 1 || map < 1 || map > 9)
	{
		return false;
	}
	if(shareware)
	{ // Shareware version checks
		if(episode != 1)
		{
			return false;
		}
	}
	else if(ExtendedWAD)
	{ // Extended version checks
		if(episode == 6)
		{
			if(map > 3)
			{
				return false;
			}
		}
		else if(episode > 5)
		{
			return false;
		}
	}
	else
	{ // Registered version checks
		if(episode == 4)
		{
			if(map != 1)
			{
				return false;
			}
		}
		else if(episode > 3)
		{
			return false;
		}
	}
	return true;
}

/*
=================
=
= M_CheckParm
=
= Checks for the given parameter in the program's command line arguments
=
= Returns the argument number (1 to argc-1) or 0 if not present
=
=================
*/

int M_CheckParm (char *check)
{
	int     i;

	for (i = 1;i<myargc;i++)
	{
		if ( !strcasecmp(check, myargv[i]) )
			return i;
	}

	return 0;
}



/*
===============
=
= M_Random
=
= Returns a 0-255 number
=
===============
*/

unsigned char rndtable[256] = {
	0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66,
	74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36,
	95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188,
	52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224,
	149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242,
	145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0,
	175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235,
	25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113,
	94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75,
	136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196,
	135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113,
	80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241,
	24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224,
	145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95,
	28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226,
	71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36,
	17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106,
	197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136,
	120, 163, 236, 249
};
int rndindex = 0;
int prndindex = 0;

int P_Random (void)
{
	prndindex = (prndindex+1)&0xff;
	return rndtable[prndindex];
}

int M_Random (void)
{
	rndindex = (rndindex+1)&0xff;
	return rndtable[rndindex];
}

void M_ClearRandom (void)
{
	rndindex = prndindex = 0;
}


void M_ClearBox (fixed_t *box)
{
	box[BOXTOP] = box[BOXRIGHT] = MININT;
	box[BOXBOTTOM] = box[BOXLEFT] = MAXINT;
}

void M_AddToBox (fixed_t *box, fixed_t x, fixed_t y)
{
	if (x<box[BOXLEFT])
		box[BOXLEFT] = x;
	else if (x>box[BOXRIGHT])
		box[BOXRIGHT] = x;
	if (y<box[BOXBOTTOM])
		box[BOXBOTTOM] = y;
	else if (y>box[BOXTOP])
		box[BOXTOP] = y;
}



/*
==================
=
= M_WriteFile
=
==================
*/

#ifndef O_BINARY
#define O_BINARY 0
#endif

boolean M_WriteFile (char const *name, void *source, int length)
{
	int handle, count;

	handle = open (name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
	if (handle == -1)
		return false;
	count = write (handle, source, length);
	close (handle);

	if (count < length)
		return false;

	return true;
}


/*
==================
=
= M_ReadFile
=
==================
*/

int M_ReadFile (char const *name, byte **buffer)
{
	int handle, count, length;
	struct stat fileinfo;
	byte        *buf;

	handle = open (name, O_RDONLY | O_BINARY, 0666);
	if (handle == -1)
		I_Error ("Couldn't read file %s", name);
	if (fstat (handle,&fileinfo) == -1)
		I_Error ("Couldn't read file %s", name);
	length = fileinfo.st_size;
	buf = Z_Malloc (length, PU_STATIC, NULL);
	count = read (handle, buf, length);
	close (handle);

	if (count < length)
		I_Error ("Couldn't read file %s", name);

	*buffer = buf;
	return length;
}

//---------------------------------------------------------------------------
//
// PROC M_FindResponseFile
//
//---------------------------------------------------------------------------

#define MAXARGVS 100

void M_FindResponseFile(void)
{
	int i;

	for(i = 1; i < myargc; i++)
	{
		if(myargv[i][0] == '@')
		{
			FILE *handle;
			int size;
			int k;
			int index;
			int indexinfile;
			char *infile;
			char *file;
			char *moreargs[20];
			char *firstargv;

			// READ THE RESPONSE FILE INTO MEMORY
			handle = fopen(&myargv[i][1], "rb");
			if(!handle)
			{
				printf("\nNo such response file!");
				exit(1);
			}
			printf("Found response file %s!\n",&myargv[i][1]);
			fseek (handle,0,SEEK_END);
			size = ftell(handle);
			fseek (handle,0,SEEK_SET);
			file = malloc (size);
			fread (file,size,1,handle);
			fclose (handle);

			// KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
			for (index = 0,k = i+1; k < myargc; k++)
				moreargs[index++] = myargv[k];
			
			firstargv = myargv[0];
			myargv = malloc(sizeof(char *)*MAXARGVS);
			memset(myargv,0,sizeof(char *)*MAXARGVS);
			myargv[0] = firstargv;
			
			infile = file;
			indexinfile = k = 0;
			indexinfile++;  // SKIP PAST ARGV[0] (KEEP IT)
			do
			{
				myargv[indexinfile++] = infile+k;
				while(k < size &&  

					((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))
					k++;
				*(infile+k) = 0;
				while(k < size &&
					((*(infile+k)<= ' ') || (*(infile+k)>'z')))
					k++;
			} while(k < size);
			
			for (k = 0;k < index;k++)
				myargv[indexinfile++] = moreargs[k];
			myargc = indexinfile;
			// DISPLAY ARGS
			if(M_CheckParm("-debug"))
			{
				printf("%d command-line args:\n", myargc);
				for(k = 1; k < myargc; k++)
				{
					printf("%s\n", myargv[k]);
				}
			}
			break;
		}
	}
}

//---------------------------------------------------------------------------
//
// PROC M_ForceUppercase
//
// Change string to uppercase.
//
//---------------------------------------------------------------------------

void M_ForceUppercase(char *text)
{
	char c;

	while((c = *text) != 0)
	{
		if(c >= 'a' && c <= 'z')
		{
			*text++ = c-('a'-'A');
		}
		else
		{
			text++;
		}
	}
}

/*
==============================================================================

							DEFAULTS

==============================================================================
*/

int     usemouse;
int     usejoystick;

extern  int     key_right, key_left, key_up, key_down;
extern  int     key_strafeleft, key_straferight;
extern  int     key_fire, key_use, key_strafe, key_speed;
extern	int		key_flyup, key_flydown, key_flycenter;
extern	int		key_lookup, key_lookdown, key_lookcenter;
extern	int		key_invleft, key_invright, key_useartifact;

extern  int         mousebfire;
extern  int         mousebstrafe;
extern  int         mousebforward;

extern  int         joybfire;
extern  int         joybstrafe;
extern  int         joybuse;
extern  int         joybspeed;

extern  int     viewwidth, viewheight;

int mouseSensitivity;

extern  int screenblocks;

extern char *chat_macros[10];

typedef struct
{
	char    *name;
	int     *location;
	int     defaultvalue;
	int     scantranslate;      // PC scan code hack
	int     untranslated;       // lousy hack
} default_t;

#ifndef __NeXT__
extern int snd_Channels;
extern int snd_DesiredMusicDevice, snd_DesiredSfxDevice;
extern int snd_MusicDevice, // current music card # (index to dmxCodes)
	snd_SfxDevice; // current sfx card # (index to dmxCodes)

extern int     snd_SBport, snd_SBirq, snd_SBdma;       // sound blaster variables
extern int     snd_Mport;                              // midi variables
#endif

default_t defaults[] =
{
	{ "mouse_sensitivity", &mouseSensitivity, 5 },

#ifndef __NeXT__
	{ "sfx_volume", &snd_MaxVolume, 127},
	{ "music_volume", &snd_MusicVolume, 127},
#endif

#ifdef __WATCOMC__
#define SC_UPARROW              0x48
#define SC_DOWNARROW            0x50
#define SC_LEFTARROW            0x4b
#define SC_RIGHTARROW           0x4d
#define SC_RCTRL                0x1d
#define SC_RALT                 0x38
#define SC_RSHIFT               0x36
#define SC_SPACE                0x39
#define SC_COMMA                0x33
#define SC_PERIOD               0x34
#define SC_PAGEUP				0x49
#define SC_INSERT				0x52
#define SC_HOME					0x47
#define SC_PAGEDOWN				0x51
#define SC_DELETE				0x53
#define SC_END					0x4f
#define SC_ENTER				0x1c

	{ "key_right", &key_right, SC_RIGHTARROW, 1 },
	{ "key_left", &key_left, SC_LEFTARROW, 1 },
	{ "key_up", &key_up, SC_UPARROW, 1 },
	{ "key_down", &key_down, SC_DOWNARROW, 1 },
	{ "key_strafeleft", &key_strafeleft, SC_COMMA, 1 },
	{ "key_straferight", &key_straferight, SC_PERIOD, 1 },
	{ "key_flyup", &key_flyup, SC_PAGEUP, 1 },
	{ "key_flydown", &key_flydown, SC_INSERT, 1 },
	{ "key_flycenter", &key_flycenter, SC_HOME, 1 },
	{ "key_lookup", &key_lookup, SC_PAGEDOWN, 1 },
	{ "key_lookdown", &key_lookdown, SC_DELETE, 1 },
	{ "key_lookcenter", &key_lookcenter, SC_END, 1 },
	{ "key_invleft", &key_invleft, 0x1a, 1 },
	{ "key_invright", &key_invright, 0x1b, 1 },
	{ "key_useartifact", &key_useartifact, SC_ENTER, 1 },

	{ "key_fire", &key_fire, SC_RCTRL, 1 },
	{ "key_use", &key_use, SC_SPACE, 1 },
	{ "key_strafe", &key_strafe, SC_RALT, 1 },
	{ "key_speed", &key_speed, SC_RSHIFT, 1 },
#endif

	#if 1
//rww end
	
/*
	//rww begin
	{ "key_right", &key_straferight, KEY_RIGHTARROW },
	{ "key_left", &key_strafeleft, KEY_LEFTARROW },
	{ "key_up", &key_up, KEY_UPARROW },
	{ "key_down", &key_down, KEY_DOWNARROW },
	{ "key_strafeleft", &key_left, ',' },
	{ "key_straferight", &key_right, '.' },
	//rww end
*/	
	//Sektor
	{ "key_right", &key_right, KEY_RIGHTARROW },
	{ "key_left", &key_left, KEY_LEFTARROW },
	{ "key_up", &key_up, KEY_UPARROW },
	{ "key_down", &key_down, KEY_DOWNARROW },
	{ "key_strafeleft", &key_strafeleft, ',' },
	{ "key_straferight", &key_straferight, '.' },
	//Sektor end
	
	//{ "key_jump", &key_jump, '/'},
	{ "key_flyup", &key_flyup, 'u' },
	{ "key_flydown", &key_flydown, 'j' },
	{ "key_flycenter", &key_flycenter, 'k' },
	{ "key_lookup", &key_lookup, 'm' },
	{ "key_lookdown", &key_lookdown, 'b' },
	{ "key_lookcenter", &key_lookcenter, 'n' },
	{ "key_invleft", &key_invleft, '[' },
	{ "key_invright", &key_invright, ']' },
	//rww begin
//	{ "key_useartifact", &key_useartifact, 13 },
	{ "key_useartifact", &key_useartifact, 'o' },
	//{ "key_weaponcycle", &key_weaponcycle, 'l' },
	//rww end

	//rww begin
	//{ "key_fire", &key_fire, ' ', 1 },
	//{ "key_use", &key_use, 'x', 1 },
	{ "key_fire", &key_fire, KEY_RCTRL, 1 },
	{ "key_use", &key_use, ' ', 1 },
	//rww end
	
	//{ "key_strafe", &key_strafe, 'c', 1 },
	{ "key_strafe", &key_strafe, 'c', 1 },
	{ "key_speed", &key_speed, 'z', 1 },
#endif

#ifdef __NeXT__
	{ "key_right", &key_right, KEY_RIGHTARROW },
	{ "key_left", &key_left, KEY_LEFTARROW },
	{ "key_up", &key_up, KEY_UPARROW },
	{ "key_down", &key_down, KEY_DOWNARROW },
	{ "key_strafeleft", &key_strafeleft, ',' },
	{ "key_straferight", &key_straferight, '.' },
	{ "key_flyup", &key_flyup, 'u' },
	{ "key_flydown", &key_flydown, 'j' },
	{ "key_flycenter", &key_flycenter, 'k' },
	{ "key_lookup", &key_lookup, 'm' },
	{ "key_lookdown", &key_lookdown, 'b' },
	{ "key_lookcenter", &key_lookcenter, 'n' },
	{ "key_invleft", &key_invleft, '[' },
	{ "key_invright", &key_invright, ']' },
	{ "key_useartifact", &key_useartifact, 13 },

	{ "key_fire", &key_fire, ' ', 1 },
	{ "key_use", &key_use, 'x', 1 },
	{ "key_strafe", &key_strafe, 'c', 1 },
	{ "key_speed", &key_speed, 'z', 1 },
#endif

	{ "use_mouse", &usemouse, 1 },
	{ "mouseb_fire", &mousebfire, 0 },
	{ "mouseb_strafe", &mousebstrafe, 1 },
	{ "mouseb_forward", &mousebforward, 2 },

	{ "use_joystick", &usejoystick, 0 },
	{ "joyb_fire", &joybfire, 0 },
	{ "joyb_strafe", &joybstrafe, 1 },
	{ "joyb_use", &joybuse, 3 },
	{ "joyb_speed", &joybspeed, 2 },

	{ "screenblocks", &screenblocks, 10 },

#if 0  //ndef __NeXT__
	{ "snd_channels", &snd_Channels, 3 },
	{ "snd_musicdevice", &snd_DesiredMusicDevice, 0 },
	{ "snd_sfxdevice", &snd_DesiredSfxDevice, 0 },
	{ "snd_sbport", &snd_SBport, 544 },
	{ "snd_sbirq", &snd_SBirq, -1 },
	{ "snd_sbdma", &snd_SBdma, -1 },
	{ "snd_mport", &snd_Mport, -1 },
#endif

	{ "usegamma", &usegamma, 0 },

	{ "chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 },
	{ "chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 },
	{ "chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 },
	{ "chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 },
	{ "chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 },
	{ "chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 },
	{ "chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 },
	{ "chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 },
	{ "chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 },
	{ "chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 }
};

int numdefaults;
char *defaultfile;

/*
==============
=
= M_SaveDefaults
=
==============
*/

void M_SaveDefaults (void)
{
	int     i,v;
	FILE    *f;

	f = fopen (defaultfile, "w");
	if (!f)
		return;         // can't write the file, but don't complain

	for (i=0 ; i<numdefaults ; i++)
	{
#ifdef __WATCOMC__
		if (defaults[i].scantranslate)
			defaults[i].location = &defaults[i].untranslated;
#endif
		if (defaults[i].defaultvalue > -0xfff
		  && defaults[i].defaultvalue < 0xfff)
		{
			v = *defaults[i].location;
			fprintf (f,"%s\t\t%i\n",defaults[i].name,v);
		} else {
			fprintf (f,"%s\t\t\"%s\"\n",defaults[i].name,
			  * (char **) (defaults[i].location));
		}
	}

	fclose (f);
}


/*
==============
=
= M_LoadDefaults
=
==============
*/

extern byte scantokey[128];
extern char *basedefault;

void M_LoadDefaults (void)
{
	int     i, len;
	FILE    *f;
	char    def[80];
	char        strparm[100];
	char    *newstring;
	int     parm;
	boolean     isstring;

//
// set everything to base values
//
	numdefaults = sizeof(defaults)/sizeof(defaults[0]);
	for (i=0 ; i<numdefaults ; i++)
		*defaults[i].location = defaults[i].defaultvalue;

//
// check for a custom default file
//
	i = M_CheckParm("-config");
	if(i && i<myargc-1)
	{
		defaultfile = myargv[i+1];
		printf("default file: %s\n", defaultfile);
	}
	else if(cdrom)
	{
		defaultfile = "c:\\heretic.cd\\heretic.cfg";
	}
	else
	{
		defaultfile = basedefault;
	}

//
// read the file in, overriding any set defaults
//
	f = fopen (defaultfile, "r");
	if (f)
	{
		while (!feof(f))
		{
			isstring = false;
			if (fscanf (f, "%79s %[^\n]\n", def, strparm) == 2)
			{
			  if (strparm[0] == '"')
			  {
				// get a string default
				isstring = true;
				len = strlen(strparm);
				newstring = (char *) malloc(len);
				strparm[len-1] = 0;
				strcpy(newstring, strparm+1);
			  }
			  else if (strparm[0] == '0' && strparm[1] == 'x')
				  sscanf(strparm+2, "%x", &parm);
			  else
				  sscanf(strparm, "%i", &parm);
			  for (i=0 ; i<numdefaults ; i++)
				  if (!strcmp(def, defaults[i].name))
				  {
					  if (!isstring)
						*defaults[i].location = parm;
					  else
						*defaults[i].location =
						  (int) newstring;
					  break;
				  }
			}
		}

		fclose (f);
	}


#ifdef __WATCOMC__
	for(i = 0; i < numdefaults; i++)
	{
		if(defaults[i].scantranslate)
		{
			parm = *defaults[i].location;
			defaults[i].untranslated = parm;
			*defaults[i].location = scantokey[parm];
		}
	}
#endif
}


/*
==============================================================================

						SCREEN SHOTS

==============================================================================
*/


typedef struct
{
	char    manufacturer;
	char    version;
	char    encoding;
	char    bits_per_pixel;
	unsigned short  xmin,ymin,xmax,ymax;
	unsigned short  hres,vres;
	unsigned char   palette[48];
	char    reserved;
	char    color_planes;
	unsigned short  bytes_per_line;
	unsigned short  palette_type;
	char    filler[58];
	unsigned char   data;           // unbounded
} pcx_t;

/*
==============
=
= WritePCXfile
=
==============
*/

void WritePCXfile (char *filename, byte *data, int width, int height, byte *palette)
{
	int     i, length;
	pcx_t   *pcx;
	byte        *pack;
	
	pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL);

	pcx->manufacturer = 0x0a;   // PCX id
	pcx->version = 5;           // 256 color
	pcx->encoding = 1;      // uncompressed
	pcx->bits_per_pixel = 8;        // 256 color
	pcx->xmin = 0;
	pcx->ymin = 0;
	pcx->xmax = SHORT(width-1);
	pcx->ymax = SHORT(height-1);
	pcx->hres = SHORT(width);
	pcx->vres = SHORT(height);
	memset (pcx->palette,0,sizeof(pcx->palette));
	pcx->color_planes = 1;      // chunky image
	pcx->bytes_per_line = SHORT(width);
	pcx->palette_type = SHORT(2);       // not a grey scale
	memset (pcx->filler,0,sizeof(pcx->filler));

//
// pack the image
//
	pack = &pcx->data;

	for (i=0 ; i<width*height ; i++)
		if ( (*data & 0xc0) != 0xc0)
			*pack++ = *data++;
		else
		{
			*pack++ = 0xc1;
			*pack++ = *data++;
		}

//
// write the palette
//
	*pack++ = 0x0c; // palette ID byte
	for (i=0 ; i<768 ; i++)
		*pack++ = *palette++;

//
// write output file
//
	length = pack - (byte *)pcx;
	M_WriteFile (filename, pcx, length);

	Z_Free (pcx);
}


//==============================================================================

/*
==================
=
= M_ScreenShot
=
==================
*/

void M_ScreenShot (void)
{
	extern byte *host_basepal;
	int     i;
	byte    *linear;
	char    lbmname[12];
	byte *pal;

#ifdef _WATCOMC_
	extern  byte *pcscreen;
#endif
//
// munge planar buffer to linear
//
#ifdef _WATCOMC_
	linear = pcscreen;
#else
	linear = screen;
#endif
//
// find a file name to save it to
//
	strcpy(lbmname,"HRTIC00.pcx");

	for (i=0 ; i<=99 ; i++)
	{
		lbmname[5] = i/10 + '0';
		lbmname[6] = i%10 + '0';
		if (access(lbmname,0) == -1)
			break;  // file doesn't exist
	}
	if (i==100)
		I_Error ("M_ScreenShot: Couldn't create a PCX");

//
// save the pcx file
//
#ifdef __WATCOMC__
	pal = (byte *)Z_Malloc(768, PU_STATIC, NULL);
	outp(0x3c7, 0);
	for(i = 0; i < 768; i++)
	{
		*(pal+i) = inp(0x3c9)<<2;
	}
#else
	//pal = (byte *)W_CacheLumpName("PLAYPAL", PU_CACHE);
#endif

	WritePCXfile (lbmname, linear, SCREENWIDTH, SCREENHEIGHT
		, host_basepal);

	players[consoleplayer].message = "SCREEN SHOT";
#ifdef __WATCOMC__
	//Z_Free(pal);
#endif
}
